#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dirent.h>
#include <limits.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <stdbool.h>
#include <errno.h>

#define MAX_STACK_SIZE 20
#define MAX_PATH_LEN PATH_MAX
#define MAX_LS_BUFFER 8192
#define MAX_COMMAND_LEN 512

// 客户端会话状态
typedef struct {
    char current_dir[MAX_PATH_LEN];   // 当前虚拟工作目录
    char root_dir[MAX_PATH_LEN];      // 沙箱根目录
    char *dir_stack[MAX_STACK_SIZE];  // 目录历史堆栈
    int stack_top;                    // 栈顶指针
} ClientSession;

// 初始化会话
void init_session(ClientSession *session, const char *root) {
    // 获取规范化根目录路径
    if (realpath(root, session->root_dir) == NULL) {
        perror("realpath failed");
        exit(EXIT_FAILURE);
    }
    
    // 确保根目录以'/'结尾
    size_t len = strlen(session->root_dir);
    if (session->root_dir[len-1] != '/') {
        if (len < MAX_PATH_LEN - 1) {
            strcat(session->root_dir, "/");
        }
    }
    
    strncpy(session->current_dir, session->root_dir, MAX_PATH_LEN);
    session->stack_top = 0;  // 初始化空栈
}

// 清理会话资源
void free_session(ClientSession *session) {
    // 释放堆栈内存
    for (int i = 0; i < session->stack_top; i++) {
        free(session->dir_stack[i]);
    }
    session->stack_top = 0;
}

// 路径规范化处理
bool normalize_path(ClientSession *session, const char *input_path, char *output_path) {
    char temp_path[MAX_PATH_LEN * 2];
    char *token;
    char *saveptr;
    char components[MAX_PATH_LEN][MAX_PATH_LEN];
    int comp_count = 0;
    
    // 处理特殊路径
    if (strcmp(input_path, "~") == 0 || strcmp(input_path, "/") == 0) {
        strncpy(output_path, session->root_dir, MAX_PATH_LEN);
        return true;
    }
    
    // 处理绝对路径
    if (input_path[0] == '/') {
        snprintf(temp_path, sizeof(temp_path), "%s%s", session->root_dir, input_path);
    } 
    // 处理相对路径
    else {
        snprintf(temp_path, sizeof(temp_path), "%s%s", session->current_dir, input_path);
    }
    
    // 分割路径组件
    token = strtok_r(temp_path, "/", &saveptr);
    while (token != NULL) {
        if (strcmp(token, ".") == 0) {
            // 忽略当前目录标记
        } 
        else if (strcmp(token, "..") == 0) {
            // 回退上级目录
            if (comp_count > 0) comp_count--;
        } 
        else {
            // 添加有效组件
            if (comp_count < MAX_PATH_LEN) {
                strncpy(components[comp_count], token, MAX_PATH_LEN - 1);
                comp_count++;
            }
        }
        token = strtok_r(NULL, "/", &saveptr);
    }
    
    // 重建规范化路径
    strcpy(output_path, session->root_dir);
    for (int i = 0; i < comp_count; i++) {
        if (strlen(output_path)) strcat(output_path, "/");
        strcat(output_path, components[i]);
    }
    
    // 确保路径在沙箱内
    if (strncmp(output_path, session->root_dir, strlen(session->root_dir)) != 0) {
        return false;
    }
    
    // 确保路径以'/'结尾（如果是目录）
    if (strlen(output_path) > 0 && output_path[strlen(output_path)-1] != '/') {
        strcat(output_path, "/");
    }
    
    return true;
}

// 检查路径是否存在且有访问权限
bool is_path_accessible(const char *path, bool is_dir) {
    // 检查路径是否存在
    if (access(path, F_OK) != 0) return false;
    
    // 检查访问权限
    if (access(path, R_OK | X_OK) != 0) return false;
    
    // 如果是目录路径，确保是目录
    if (is_dir) {
        struct stat path_stat;
        if (stat(path, &path_stat)) return false;
        return S_ISDIR(path_stat.st_mode);
    }
    
    return true;
}

// 压栈：保存当前目录
bool push_dir(ClientSession *session) {
    if (session->stack_top >= MAX_STACK_SIZE) {
        fprintf(stderr, "Directory stack overflow\n");
        return false;
    }
    
    session->dir_stack[session->stack_top] = strdup(session->current_dir);
    if (session->dir_stack[session->stack_top] == NULL) {
        perror("strdup failed");
        return false;
    }
    
    session->stack_top++;
    return true;
}

// 弹栈：获取历史目录
char *pop_dir(ClientSession *session) {
    if (session->stack_top <= 0) {
        fprintf(stderr, "Directory stack underflow\n");
        return NULL;
    }
    
    session->stack_top--;
    return session->dir_stack[session->stack_top];
}

// 处理cd命令
bool handle_cd(ClientSession *session, const char *path) {
    char target[MAX_PATH_LEN];
    char old_dir[MAX_PATH_LEN];
    strncpy(old_dir, session->current_dir, MAX_PATH_LEN);

    // 特殊命令：cd - (返回上一级)
    if (strcmp(path, "-") == 0) {
        if (session->stack_top == 0) {
            fprintf(stderr, "No previous directory\n");
            return false;
        }
        
        char *prev_dir = pop_dir(session);
        if (!prev_dir) return false;
        
        if (!is_path_accessible(prev_dir, true)) {
            fprintf(stderr, "Previous directory inaccessible\n");
            free(prev_dir);
            return false;
        }
        
        // 更新当前目录
        strncpy(session->current_dir, prev_dir, MAX_PATH_LEN);
        free(prev_dir);
        
        return true;
    }
    
    // 规范化路径
    if (!normalize_path(session, path, target)) {
        fprintf(stderr, "Invalid path: %s\n", path);
        return false;
    }
    
    // 检查路径可访问性
    if (!is_path_accessible(target, true)) {
        fprintf(stderr, "Path not accessible: %s\n", target);
        return false;
    }

    // 保存当前目录到堆栈
    if (!push_dir(session)) return false;
    
    // 更新当前目录
    strncpy(session->current_dir, target, MAX_PATH_LEN);
    return true;
}

// 处理pwd命令
const char *handle_pwd(ClientSession *session) {
    // 返回相对于根目录的路径（隐藏真实路径）
    size_t root_len = strlen(session->root_dir);
    const char *relative_path = session->current_dir + root_len;
    
    // 如果当前在根目录，返回"/"
    if (*relative_path == '\0') {
        return "/";
    }
    
    return relative_path;
}

// 处理ls命令
char *handle_ls(ClientSession *session) {
    DIR *dir = opendir(session->current_dir);
    if (!dir) {
        perror("opendir failed");
        return NULL;
    }

    struct dirent *entry;
    char *result = (char *)malloc(MAX_LS_BUFFER);
    if (!result) {
        perror("malloc failed");
        closedir(dir);
        return NULL;
    }
    
    result[0] = '\0';
    char temp[256];
    size_t remaining = MAX_LS_BUFFER - 1;
    int count = 0;

    while ((entry = readdir(dir)) != NULL) {
        // 跳过隐藏文件
        if (entry->d_name[0] == '.') continue;
        
        // 获取文件类型标记
        char type = ' ';
        if (entry->d_type == DT_DIR) {
            type = '/';
        } else if (entry->d_type == DT_LNK) {
            type = '@';
        } else if (entry->d_type == DT_FIFO) {
            type = '|';
        } else if (entry->d_type == DT_SOCK) {
            type = '=';
        }
        
        // 检查缓冲区空间
        int needed = snprintf(temp, sizeof(temp), "%-24s%c\n", entry->d_name, type);
        if (needed < 0 || (size_t)needed >= remaining) {
            // 缓冲区不足，截断输出
            break;
        }
        
        strcat(result, temp);
        remaining -= needed;
        count++;
    }
    closedir(dir);
    
    if (count == 0) {
        strcpy(result, "No files or directories\n");
    }
    
    return result;  // 调用者需free()
}

// 处理mkdir命令
bool handle_mkdir(ClientSession *session, const char *path) {
    char target[MAX_PATH_LEN];
    
    // 规范化路径
    if (!normalize_path(session, path, target)) {
        fprintf(stderr, "Invalid path: %s\n", path);
        return false;
    }
    
    // 移除结尾的斜杠（mkdir需要）
    size_t len = strlen(target);
    if (len > 0 && target[len-1] == '/') {
        target[len-1] = '\0';
    }
    
    // 检查目录是否已存在
    if (access(target, F_OK) == 0) {
        fprintf(stderr, "Directory already exists: %s\n", target);
        return false;
    }
    
    // 创建目录（权限：用户读写执行）
    if (mkdir(target, 0700)) {
        perror("mkdir failed");
        return false;
    }
    
    return true;
}

// 处理rmdir命令（仅删除空目录）
bool handle_rmdir(ClientSession *session, const char *path) {
    char target[MAX_PATH_LEN];
    
    // 规范化路径
    if (!normalize_path(session, path, target)) {
        fprintf(stderr, "Invalid path: %s\n", path);
        return false;
    }
    
    // 移除结尾的斜杠
    size_t len = strlen(target);
    if (len > 0 && target[len-1] == '/') {
        target[len-1] = '\0';
    }
    
    // 检查目录是否存在
    if (access(target, F_OK) != 0) {
        fprintf(stderr, "Directory does not exist: %s\n", target);
        return false;
    }
    
    // 检查是否为目录
    struct stat path_stat;
    if (stat(target, &path_stat) || !S_ISDIR(path_stat.st_mode)) {
        fprintf(stderr, "Not a directory: %s\n", target);
        return false;
    }
    
    // 检查目录是否为空
    DIR *dir = opendir(target);
    if (!dir) {
        perror("opendir failed");
        return false;
    }
    
    struct dirent *entry;
    int file_count = 0;
    while ((entry = readdir(dir)) != NULL) {
        if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
            file_count++;
            break;
        }
    }
    closedir(dir);
    
    if (file_count > 0) {
        fprintf(stderr, "Directory not empty: %s\n", target);
        return false;
    }
    
    // 删除空目录
    if (rmdir(target)) {
        perror("rmdir failed");
        return false;
    }
    
    return true;
}

// 命令处理器
void process_command(ClientSession *session, const char *command) {
    char cmd[32];
    char path[MAX_PATH_LEN] = {0};
    char full_command[MAX_COMMAND_LEN];
    
    // 复制命令以避免修改原始数据
    strncpy(full_command, command, MAX_COMMAND_LEN - 1);
    full_command[MAX_COMMAND_LEN - 1] = '\0';
    
    // 解析命令
    if (sscanf(full_command, "%31s %[^\n]", cmd, path) < 1) {
        fprintf(stderr, "Invalid command format\n");
        return;
    }
    
    if (strcmp(cmd, "cd") == 0) {
        if (strlen(path) == 0) {
            // 默认cd：返回根目录
            if (!handle_cd(session, "~")) {
                printf("ERROR: Directory change failed\n");
            }
        } else {
            if (!handle_cd(session, path)) {
                printf("ERROR: Directory change failed\n");
            }
        }
        printf("CWD: %s\n", handle_pwd(session));
        
    } else if (strcmp(cmd, "pwd") == 0) {
        printf("Current directory: %s\n", handle_pwd(session));
        
    } else if (strcmp(cmd, "ls") == 0) {
        char *list = handle_ls(session);
        if (list) {
            printf("Directory listing:\n%s", list);
            free(list);
        } else {
            printf("ERROR: Failed to list directory\n");
        }
        
    } else if (strcmp(cmd, "mkdir") == 0) {
        if (strlen(path) == 0) {
            printf("ERROR: Missing directory name\n");
        } else {
            if (handle_mkdir(session, path)) {
                printf("SUCCESS: Directory created\n");
            } else {
                printf("ERROR: Failed to create directory\n");
            }
        }
        
    } else if (strcmp(cmd, "rmdir") == 0) {
        if (strlen(path) == 0) {
            printf("ERROR: Missing directory name\n");
        } else {
            if (handle_rmdir(session, path)) {
                printf("SUCCESS: Directory removed\n");
            } else {
                printf("ERROR: Failed to remove directory\n");
            }
        }
        
    } else {
        printf("ERROR: Unknown command '%s'\n", cmd);
    }
}

// 测试函数
int main() {
    ClientSession session;
    init_session(&session, "/home/muridae/DraftsJ/TestsJ/Private_protocol_file_management_server_projectJ/PPFMSPJ/Tests1J/tmp/safe_root");
    
    // 创建测试环境
    mkdir("/home/muridae/DraftsJ/TestsJ/Private_protocol_file_management_server_projectJ/PPFMSPJ/Tests1J/tmp/safe_root", 0700);
    mkdir("/home/muridae/DraftsJ/TestsJ/Private_protocol_file_management_server_projectJ/PPFMSPJ/Tests1J/tmp/safe_root/documents", 0700);
    mkdir("/home/muridae/DraftsJ/TestsJ/Private_protocol_file_management_server_projectJ/PPFMSPJ/Tests1J/tmp/safe_root/projects", 0700);
    
    printf("--- Starting session in: %s ---\n", handle_pwd(&session));
    
    // 测试命令序列
    printf("\n=== Testing cd command ===\n");
    process_command(&session, "cd documents");
    process_command(&session, "cd ..");
    process_command(&session, "cd ./projects");
    process_command(&session, "cd ../documents");
    process_command(&session, "cd -");
    process_command(&session, "cd /");
    process_command(&session, "cd ~");
    
    printf("\n=== Testing pwd command ===\n");
    process_command(&session, "pwd");
    process_command(&session, "cd documents");
    process_command(&session, "pwd");
    
    printf("\n=== Testing ls command ===\n");
    process_command(&session, "ls");
    process_command(&session, "cd /");
    process_command(&session, "ls");
    
    printf("\n=== Testing mkdir command ===\n");
    process_command(&session, "mkdir new_folder");
    process_command(&session, "mkdir ./documents/sub_folder");
    process_command(&session, "mkdir /new_abs_folder");
    process_command(&session, "mkdir existing_folder");  // 应该失败
    
    printf("\n=== Testing rmdir command ===\n");
    process_command(&session, "rmdir new_folder");
    process_command(&session, "rmdir /new_abs_folder");
    process_command(&session, "rmdir documents");  // 非空目录应该失败
    process_command(&session, "rmdir invalid_folder");  // 不存在应该失败
    
    printf("\n=== Testing special paths ===\n");
    process_command(&session, "cd .");
    process_command(&session, "cd ..");
    process_command(&session, "cd ../..");
    process_command(&session, "cd ~/documents");
    process_command(&session, "cd /projects");
    
    printf("\n=== Testing error cases ===\n");
    process_command(&session, "cd /invalid/path");
    process_command(&session, "mkdir");
    process_command(&session, "rmdir");
    process_command(&session, "invalid_command");
    
    free_session(&session);
    return 0;
}
